#ifndef _D3D11_H
#define _D3D11_H

#if C2504
#pragma message("D3D11Client.h")
#endif

#include "Resources.h"

#include "D3D11Pad.h"
#include "GDIPad.h"
#include "Overlay.h"
#include "Scene.h"
#include "Texture.h"
#include "Mesh.h"
#include "ShaderManager.h"

class D3D11Config;
class D3D11Client;
class MeshManager;
class TextureMgr;
class Scene;
class GDIPad;
class Overlay;

#define GET_SHADER(gc,fname,entry,res) (gc->GetShader(_T(fname), _T(entry), res))
#define GET_SHADER_DEFINES(d3d,fname,entry,res,defines) (d3d->GetShader(_T(fname), _T(entry), res, defines))

DWORD WINAPI RenderThreadProc( void *data );

LRESULT CALLBACK _ConfigProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
LRESULT CALLBACK _GeneralProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
LRESULT CALLBACK _VesselProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
LRESULT CALLBACK _PlanetsProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );

class VideoTab {
	friend class D3D11Client;
public:
	VideoTab( D3D11Client *_gc, D3D11Config *_cfg, HWND _hTab, HINSTANCE _hDLL );
	~VideoTab();

	void UpdateConfig();//refrestvideodata.

	BOOL LaunchpadVideoWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );

	LRESULT ConfigProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
	LRESULT GeneralProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
	LRESULT VesselProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
	LRESULT PlanetProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );

	HWND
		hConfig, 
		hGeneral,
		hVessels,
		hPlanets;
private:
	void InitVideoTab();
	void SelectWidth();
	void SelectHeight();
	void SelectFullscreen( bool fullscreen );
	void SelectOutput();
	void SelectMode();
	void SelectFullScreenWindow();
	void SelectSoftwareDevice();
	void InitTextBoxes();

	void CreateSymbolicLinks();
	bool SymbolicLinksCreated();
	bool CreateLink(const char * linkName, const char * destination);

	void InitConfigWindow();
	void InitGeneralWindow();
	void InitVesselWindow();
	void InitPlanetWindow();

	void InitConfig();
	void SaveConfig();

	oapi::GraphicsClient::VIDEODATA *vdata;

	DXGI_MODE_DESC *ModeDesc;
	std::vector<int> m_modeDescIndices;

	D3D11Client *gc;
	D3D11Config *cfg;
	HWND hTab;
	int aspect, aspect_wfac[3], aspect_hfac[3];

	HINSTANCE hDLL;
	D3D11CONFIG Cfg;
	RECT InitTabRect;
};

class D3D11Client : public oapi::GraphicsClient {
	friend class VideoTab;
	friend class Scene;
public:
	D3D11Client( HINSTANCE hIn );
	~D3D11Client();

	void ToggleHUD();

	bool clbkUseLaunchpadVideoTab() const;
	void clbkRefreshVideoData();
//	bool clbkGetRenderParam( DWORD param, DWORD *value );
//render/windows.
	BOOL LaunchpadVideoWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
	LRESULT RenderWndProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp );
	void clbkGetViewportSize( DWORD *w, DWORD *h ) const;
	bool clbkFullscreenMode() const;	//	-> D3D11Client
	bool clbkInitialise();				//	-> D3D11Client
	HWND clbkCreateRenderWindow();		//	-> D3D11Client
	void clbkPostCreation();			//	-> D3D11Client
//popups.
	void clbkPreOpenPopup();		//?
	bool RenderWithPopupWindows();	//?

	bool clbkFullScreenMode() const;// = 0
//	void clbkGetViewportSize( DWORD *w, DWORD *h ) const;// = 0
	bool clbkGetRenderParam( DWORD prm, DWORD *value ) const;// = 0
//GDI.
	oapi::Sketchpad* clbkGetSketchpad( SURFHANDLE surf );				//	-> GDIPad
	void clbkReleaseSketchpad( oapi::Sketchpad *Skpad );				//	-> GDIPad
	oapi::Font* clbkCreateFont( int height, bool prop, const char *face, oapi::Font::Style style, int orientation ) const;//	-> GDIPad
	void clbkReleaseFont( oapi::Font *font ) const;						//	-> GDIPad
	oapi::Pen* clbkCreatePen( int style, int width, DWORD col ) const;	//	-> GDIPad
	void clbkReleasePen( oapi::Pen *pen ) const;						//	-> GDIPad
	oapi::Brush* clbkCreateBrush( DWORD col ) const;					//	-> GDIPad
	void clbkReleaseBrush( oapi::Brush *brush ) const;					//	-> GDIPad
//textures and surfaces.
//	SURFHANDLE clbkCreateSurface( HBITMAP hbmp );							//	-> TextureManager
	SURFHANDLE clbkCreateSurface( DWORD w, DWORD h, SURFHANDLE tpl = NULL );//	-> TextureManager
	SURFHANDLE clbkCreateTexture( DWORD w, DWORD h );						//	-> TextureManager
	virtual SURFHANDLE clbkCreateSurfaceEx (DWORD w, DWORD h, DWORD attrib);//  -> TextureManager
	void clbkIncrSurfaceRef( SURFHANDLE srf );							//	-> TextureManager
	bool clbkReleaseSurface( SURFHANDLE srf );						//	-> TextureManager
	void clbkReleaseTexture( SURFHANDLE tex );
	bool clbkGetSurfaceSize( SURFHANDLE srf, DWORD *w, DWORD *h );		//	-> TextureManager
	bool clbkSetSurfaceColourKey( SURFHANDLE srf, DWORD ckey );			//	-> TextureManager
	DWORD clbkGetDeviceColour( BYTE r, BYTE g, BYTE b );				//	-> TextureManager
	HDC clbkGetSurfaceDC( SURFHANDLE srf );								//	-> TextureManager
	void clbkReleaseSurfaceDC( SURFHANDLE srf, HDC hdc );				//	-> TextureManager
	bool clbkBlt(	SURFHANDLE tgt, DWORD tgtx, DWORD tgty, 
					SURFHANDLE src, DWORD flag ) const;					//	-> TextureManager
	bool clbkBlt(	SURFHANDLE tgt, DWORD tgtx, DWORD tgty, 
					SURFHANDLE src, DWORD srcx, DWORD srcy, 
					DWORD w, DWORD h, DWORD flag ) const;				//	-> TextureManager
	bool clbkScaleBlt(	SURFHANDLE tgt, DWORD tgtx, DWORD tgty, DWORD tgtw, DWORD tgth,
						SURFHANDLE src, DWORD srcx, DWORD srcy, DWORD srcw, DWORD srch, 
						DWORD flag ) const;								//	-> TextureManager
//	bool clbkCopyBitmap( SURFHANDLE srf, HBITMAP hbm, int x, int y, int dx, int dy );//	-> TextureManager
	bool clbkFillSurface( SURFHANDLE srf, DWORD col ) const;			//	-> TextureManager
	bool clbkFillSurface( SURFHANDLE srf, DWORD tgtx, DWORD tgty, DWORD w, DWORD h, DWORD col ) const;//	-> TextureManager
//vessels.
	void clbkNewVessel( OBJHANDLE hVessel );
	int clbkVisEvent( OBJHANDLE hObj, VISHANDLE vis, DWORD msg, UINT context );
	void clbkDeleteVessel( OBJHANDLE hVessel );
//meshes.
	MESHHANDLE clbkGetMesh( VISHANDLE vis, UINT idx );
	void clbkStoreMeshPersistent( MESHHANDLE hMesh, const char *fname );
	int clbkEditMeshGroup( DEVMESHHANDLE hMesh, DWORD didx, GROUPEDITSPEC *ges );
	bool clbkSetMeshTexture( DEVMESHHANDLE hMesh, DWORD texidx, SURFHANDLE tex );
	int clbkSetMeshMaterial( DEVMESHHANDLE hMesh, DWORD matidx, const MATERIAL *mat );
	bool clbkSetMeshProperty( DEVMESHHANDLE hMesh, DWORD prop, DWORD value );
//particle streams.
	oapi::ParticleStream *clbkCreateParticleStream( PARTICLESTREAMSPEC *pss ) { return NULL; };
	oapi::ParticleStream *clbkCreateExhaustStream( PARTICLESTREAMSPEC *pss, OBJHANDLE obj, const double *lvl, const VECTOR3 *ref, const VECTOR3 *dir );
	oapi::ParticleStream *clbkCreateExhaustStream( PARTICLESTREAMSPEC *pss, OBJHANDLE obj, const double *lvl, const VECTOR3 &ref, const VECTOR3 &dir );
	oapi::ParticleStream *clbkCreateReentryStream( PARTICLESTREAMSPEC *pss,	OBJHANDLE obj );
	bool clbkParticleStreamExists( const oapi::ParticleStream *ps );

	HWND hWindow;
	void WaitForFrameSync();

	Scene *GetScene() { return SC; };
	D3D11Config *Cfg() { return cfg; };
	TextureMgr * GetTextureMgr() { return TM; };

	void ProgressString( const char *line, DWORD _color );

	bool bInLoading;
	Texture *HUDTEX;

	template <typename T>
	bool GetShader(const std::string fileName, const std::string entryPoint, CComPtrEx<T>& result, const D3D10_SHADER_MACRO* pDefines = nullptr)
	{
		return shaderManager->GetShader<T>(fileName, entryPoint, result, pDefines);
	}
	template<typename T>
	bool GetShaderBytecode(const CComPtrEx<T>& shaderObject, CComPtrEx<ID3DBlob>& bytecode)
	{
		return shaderManager->GetShaderBytecode(shaderObject, bytecode);
	}
	template<typename T, size_t TLayoutSize>
	bool CreateInputLayout(D3D11_INPUT_ELEMENT_DESC (&layout)[TLayoutSize], const CComPtrEx<T>& shaderObject, CComPtrEx<ID3D11InputLayout>& result)
	{
		CComPtrEx<ID3DBlob> bytecode;
		if (!GetShaderBytecode(shaderObject, bytecode))
			return false;
		UINT numElements = TLayoutSize;
		auto hr = Dev->CreateInputLayout(layout, numElements, bytecode->GetBufferPointer(), bytecode->GetBufferSize(), &result);
		return SUCCEEDED(hr);
	}
protected:
	TextureMgr *TM;
	std::unique_ptr<ShaderManager> shaderManager;

	virtual int clbkBeginBltGroup (SURFHANDLE tgt) { return 0; }
	virtual int clbkEndBltGroup () { return 0; }
	bool clbkCopyBitmap( SURFHANDLE srf, HBITMAP hbm, int x, int y, int dx, int dy );//	-> TextureManager
	SURFHANDLE clbkCreateSurface( HBITMAP hbmp );							//	-> TextureManager
	SURFHANDLE clbkLoadTexture( const char *fname, DWORD flags );
	virtual SURFHANDLE clbkLoadSurface (const char *fname, DWORD attrib);
//render.
	void clbkUpdate( bool running );
	void clbkRenderScene();// = 0
	bool clbkDisplayFrame();
	void clbkRender2DPanel( SURFHANDLE *srf, MESHHANDLE hMesh, MATRIX3 *T, bool transparent );
//exit.
	void clbkCloseSession( bool fastclose );
	void clbkDestroyRenderWindow( bool fastclose );
//misc.
	oapi::ScreenAnnotation *clbkCreateAnnotation();
private:
	bool m_displayHUD;
	//splash-screen related
	struct SVTX {
		D3DXVECTOR2 pos;
		D3DXVECTOR2 tex;
	} quad[4];
	bool sscreen_init;
	UINT ypos;
	oapi::Font *text_Font;
	Texture *text_Surface, *SplashScreen;
	void InitSplashScreen();	
	void ExitSplashScreen();

	//threading
	DWORD ThreadID, DCount;
	GDIPad *GPad;
	VideoTab *VTab;
	bool bVideoTab, bEnumerate, bPopup;

	char wname[128];
	bool m_modeChanged;
	DEVMODE m_currentMode;
};

extern D3D11Client *gc;

#endif